package com.mtw.bbs.bbsUser.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mtw.bbs.bbsUser.mapper.UserMapper;
import com.mtw.bbs.bbsUser.pojo.entity.User;
import com.mtw.bbs.common.core.util.ExecutorUtilz;
import com.mtw.bbs.common.redisBase.util.RedisUtilz;
import com.mtw.bbs.common.redisBase.util.TimeUtilz;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mtw.bbs.bbsUser.pojo.entity.UserFollow;
import com.mtw.bbs.userAdmin.pojo.vo.UserInfoVo;
import com.mtw.bbs.bbsUser.mapper.UserFollowMapper;
import com.mtw.bbs.bbsUser.service.UserFollowService;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.mtw.bbs.bbsUser.constant.Constant.LOCK_FOLLOW_PREFIX;
import static com.mtw.bbs.bbsUser.constant.Constant.USERR_FOLLOW_KEY;

/**
 * 用户关注逻辑层
 * @author mo
 * @email mo
 * @date 2023-10-18 15:44:13
 */
@RequiredArgsConstructor
@Service("userFollowService")
public class UserFollowServiceImpl extends ServiceImpl<UserFollowMapper, UserFollow> implements UserFollowService {

    private final UserMapper userMapper;
    private final ExecutorUtilz executorUtilz;
    private final RedissonClient redissonClient;
    private final UserFollowMapper userFollowMapper;
    private final RedisUtilz<String,String> redisUtilz;




    /**
     * 关注一个用户
     * @param userId    用户id
     * @param flowedUserId    被关注用户id
     */
    @Override
    @Caching(evict = {
            @CacheEvict(value = "fans", key = "#flowedUserId")
    })
    public boolean followUser(String userId,String flowedUserId) {

        RLock lock = redissonClient.getLock(LOCK_FOLLOW_PREFIX + userId);
        lock.lock();
        try {

            String redisKey = USERR_FOLLOW_KEY + userId;
            // 缓存是否已存在
            Double score = redisUtilz.score(redisKey, flowedUserId);
            if (score != null){
                return false;
            }
            // 删除缓存
            redisUtilz.delete(redisKey);


            LambdaQueryWrapper<UserFollow> lqw = new LambdaQueryWrapper<>();
            lqw.eq(UserFollow::getFollowId,flowedUserId);
            lqw.eq(UserFollow::getUserId,userId);
            // 是否已关注
            Long count = this.count(lqw);
            if (count > 0){
                return false;
            }


            UserFollow entity = new UserFollow();
            entity.setFollowId(flowedUserId);
            entity.setUserId(userId);
            entity.setCreatedDate(new Date());
            boolean save = save(entity);
            executorUtilz.delayedTask(()-> redisUtilz.delete(redisKey));
            return save;
        }finally {
            lock.unlock();
        }
    }

    /**
     * 取消关注
     * @param userId    用户id
     * @param flowedUserId    被关注用户id
     */
    @Override
    @Caching(evict = {
            @CacheEvict(value = "fans", key = "#flowedUserId")
    })
    public boolean cancelFollowUser(String userId,String flowedUserId) {

        RLock lock = redissonClient.getLock(LOCK_FOLLOW_PREFIX + userId);
        lock.lock();
        try {

            String redisKey = USERR_FOLLOW_KEY + userId;
            // 删除缓存
            redisUtilz.delete(redisKey);

            LambdaQueryWrapper<UserFollow> lqw = new LambdaQueryWrapper<>();
            lqw.eq(UserFollow::getFollowId,flowedUserId);
            lqw.eq(UserFollow::getUserId,userId);

            int deleted = userFollowMapper.delete(lqw);
            if (deleted > 0){
                executorUtilz.delayedTask(()-> redisUtilz.delete(redisKey));
                return true;
            }
            return false;
        }finally {
            lock.unlock();
        }
    }


    /**
     * 用户关注的列表
     * @param userId    用户id
     * @param page      当前页
     * @param size      分页大小
     */
    @Override
    public Page<UserInfoVo> getUserFollowPage(String userId,int page,int size) {

        String redisKey = USERR_FOLLOW_KEY + userId;
        int start = (page - 1) * size;
        int end = page * size - 1;
        Set<ZSetOperations.TypedTuple<String>> set = redisUtilz.reverseRangeWithScores(redisKey, start, end);

        List<String> ids = null;
        Page<UserInfoVo> voPage = null;
        if (set != null){
            ids  = set.stream().map(ZSetOperations.TypedTuple::getValue).toList();
            voPage = new Page<>(page,size,redisUtilz.sizeZset(redisKey));
        }else {
            LambdaQueryWrapper<UserFollow> lqw = new LambdaQueryWrapper<>();
            lqw.select(UserFollow::getFollowId);
            lqw.eq(UserFollow::getUserId,userId);
            lqw.orderByDesc(UserFollow::getCreatedDate);
            Page<UserFollow> entityPage =  page(new Page<>(page, size), lqw);
            ids = entityPage.getRecords().stream().map(UserFollow::getFollowId).toList();
            voPage = new Page<>(page,size,entityPage.getTotal());
        }

        if (CollectionUtil.isEmpty(ids)){
            return voPage;
        }


        // 已关注的用户
        List<User> follows = userMapper.selectBatchIds(ids);
        List<UserInfoVo> infoVos = follows.stream().map(u -> {
            UserInfoVo vo = new UserInfoVo();
            BeanUtils.copyProperties(u, vo);
            Integer each = this.getIsFollowed(u.getId(), userId);
            vo.setFollowed(each!=null?2:1);
            return vo;
        }).collect(Collectors.toList());
        voPage.setRecords(infoVos);
        return voPage;
    }



    /**
     * 用户关注的uid
     *
     */
    @Override
    public List<String> getUserFollowList(String userId) {

        List<String> ids = new ArrayList<>();
        if (StringUtils.isBlank(userId)){
            return ids;
        }
        String redisKey = USERR_FOLLOW_KEY + userId;
        Set<ZSetOperations.TypedTuple<String>> set = redisUtilz.reverseRangeWithScores(redisKey, 0, 1000);

        if (set != null){
            ids  = set.stream().map(ZSetOperations.TypedTuple::getValue).toList();
        }else {
            LambdaQueryWrapper<UserFollow> lqw = new LambdaQueryWrapper<>();
            lqw.select(UserFollow::getFollowId);
            lqw.eq(UserFollow::getUserId,userId);
            lqw.orderByDesc(UserFollow::getCreatedDate);
            Page<UserFollow> entityPage =  page(new Page<>(1, 1000), lqw);
            ids = entityPage.getRecords().stream().map(UserFollow::getFollowId).toList();
        }
        return ids;
    }


    /**
     * 用户粉丝的列表
     * @param userId    用户id
     * @param page      当前页
     * @param size      分页大小
     */
    @Override
    public Page<UserInfoVo> getUserFansList(String userId,int page,int size) {

        LambdaQueryWrapper<UserFollow> lqw = new LambdaQueryWrapper<>();
        lqw.select(UserFollow::getUserId);
        lqw.eq(UserFollow::getFollowId,userId);
        lqw.orderByDesc(UserFollow::getCreatedDate);
        Page<UserFollow> entityPage = page(new Page<>(page, size), lqw);

        Page<UserInfoVo> voPage = new Page<>();
        BeanUtils.copyProperties(entityPage,voPage);
        if (!entityPage.getRecords().isEmpty()){
            List<User> follows = userMapper.selectBatchIds(entityPage.getRecords().stream().map(UserFollow::getUserId).collect(Collectors.toList()));

            List<UserInfoVo> infoVos = follows.stream().map(u -> {
                UserInfoVo vo = new UserInfoVo();
                BeanUtils.copyProperties(u, vo);
                Integer each = this.getIsFollowed(u.getId(), userId);
                vo.setFollowed(each!=null?2:1);
                return vo;
            }).collect(Collectors.toList());
            voPage.setRecords(infoVos);
        }
        return voPage;
    }





    /**
     * 重建缓存
     * @param userId    用户id
     */
    private void resetCache(String userId){

        String redisKey = USERR_FOLLOW_KEY + userId;
        // 重建缓存
        LambdaQueryWrapper<UserFollow> lqw = new LambdaQueryWrapper<>();
        lqw.select(UserFollow::getFollowId,UserFollow::getCreatedDate);
        lqw.eq(UserFollow::getUserId,userId);
        List<UserFollow> list = list(lqw);
        HashSet<ZSetOperations.TypedTuple<String>> set = new HashSet<>(list.size());
        for (UserFollow x : list) {
            ZSetOperations.TypedTuple<String> typedTuple = new DefaultTypedTuple<>(x.getFollowId(), (double) x.getCreatedDate().getTime());
            set.add(typedTuple);
        }
        if (CollectionUtil.isEmpty(list)){
            ZSetOperations.TypedTuple<String> typedTuple = new DefaultTypedTuple<>("", 0d);
            set.add(typedTuple);
        }
        redisUtilz.batchAddZset(redisKey,set);
        redisUtilz.expire(redisKey,60+ TimeUtilz.getRandomMinutes(), TimeUnit.MINUTES);
    }


    /**
     * 用户关注的数量
     * @param userId     用户id
     */
    @Override
    public Integer countOfFollow(String userId) {

        String redisKey = USERR_FOLLOW_KEY + userId;
        if (!redisUtilz.hasKey(redisKey)){
            this.resetCache(userId);
        }
        Long count = redisUtilz.sizeZset(redisKey);
        return count.intValue();
    }


    /**
     * 用户被关注的数量
     *
     * @param flowedUserId 被关注的用户
     */
    @Override
    @Cacheable(value = "fans", key = "#flowedUserId")
    public Integer countOfFans(String flowedUserId) {
        LambdaQueryWrapper<UserFollow> lqw = new LambdaQueryWrapper<>();
        lqw.select(UserFollow::getFollowId);
        lqw.eq(UserFollow::getFollowId,flowedUserId);
        Long count = count(lqw);
        return count.intValue();
    }


    /**
     * 用户是否已关注某位用户
     * @param userId            用户id
     * @param flowedUserId      被关注用户id
     * @return                  0：未关注，1：已关注
     */
    @Override
    public Integer getIsFollowed(String userId, String flowedUserId) {

        String redisKey = USERR_FOLLOW_KEY + userId;
        if (!redisUtilz.hasKey(redisKey)){
            this.resetCache(userId);
        }

        return  redisUtilz.score(redisKey,flowedUserId) == null?0:1;
    }



    /**
     * 用户是否相互关注
     * @param userId            用户id
     * @param flowedUserId      被关注用户id
     * @return                  0：未关注，1：已关注，2：相互关注
     */
    @Override
    public Integer getIsEachFollowed(String userId, String flowedUserId) {

        String redisKeyU = USERR_FOLLOW_KEY + userId;
        String redisKeyF = USERR_FOLLOW_KEY + flowedUserId;
        if (redisUtilz.hasKey(redisKeyU)){
            this.resetCache(userId);
        }
        if (redisUtilz.hasKey(redisKeyF)){
            this.resetCache(flowedUserId);
        }
        int result = redisUtilz.score(redisKeyU,flowedUserId) == null?0:1;
        result += redisUtilz.score(redisKeyF,userId) == null?0:1;
        return result;
    }



}
